package com.tql.mall.search.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.tql.mall.boss.client.BossFeignClient;
import com.tql.mall.common.constant.RedisConstant;
import com.tql.mall.merchant.client.MerchantFeignClient;
import com.tql.mall.model.merchant.ShopInfo;
import com.tql.mall.model.product.BaseAttrInfo;
import com.tql.mall.model.product.BaseCategoryView;
import com.tql.mall.model.product.BaseTrademark;
import com.tql.mall.model.product.SkuInfo;
import com.tql.mall.model.search.*;
import com.tql.mall.product.client.ProductFeignClient;
import com.tql.mall.search.repository.GoodsRepository;
import com.tql.mall.search.service.SearchService;
import lombok.SneakyThrows;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author: tyk
 * @Date: 2023/5/23 19:29
 * @Description:
 */
@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private BossFeignClient bossFeignClient;

    @Autowired
    private MerchantFeignClient merchantFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;


    @Autowired
    private RestHighLevelClient highLevelClient;

    @Override
    public Boolean onSale(Long skuId) {

        // 封装ES实体
        Goods goods = new Goods();

        // 设置skuInfo
        SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
        if (skuInfo != null) {
            goods.setId(skuId);
            goods.setDefaultImg(skuInfo.getSkuDefaultImg());
            goods.setTitle(skuInfo.getSkuName());
            goods.setPrice(productFeignClient.getSkuPrice(skuId).doubleValue());
            goods.setCreateTime(new Date());

            // 设置品牌信息
            goods.setTmId(skuInfo.getTmId());
            BaseTrademark tm = bossFeignClient.getTrademarkById(skuInfo.getTmId());
            if (tm != null) {
                goods.setTmName(tm.getTmName());
                goods.setTmLogoUrl(tm.getLogoUrl());
            }

            // 设置平台分类信息
            BaseCategoryView cv = bossFeignClient.getCategoryView(skuInfo.getCategory3Id());
            if (cv != null) {
                goods.setCategory1Id(cv.getCategory1Id());
                goods.setCategory2Id(cv.getCategory2Id());
                goods.setCategory3Id(cv.getCategory3Id());
                goods.setCategory1Name(cv.getCategory1Name());
                goods.setCategory2Name(cv.getCategory2Name());
                goods.setCategory3Name(cv.getCategory3Name());
            }

            // 设置店铺信息
            ShopInfo shop = merchantFeignClient.getShopInfoEntity(skuInfo.getShopId());
            if (shop != null) {
                goods.setShopId(shop.getId());
                goods.setShopName(shop.getName());
            }
        }

        // 设置平台属性信息
        List<BaseAttrInfo> infoList = productFeignClient.getBaseAttrInfoList(skuId);
        if (!CollectionUtils.isEmpty(infoList)) {
            List<SearchAttr> list = infoList.stream().map(e -> {
                SearchAttr sa = new SearchAttr();
                sa.setAttrId(e.getId());            // 属性ID
                sa.setAttrName(e.getAttrName());    // 属性名称
                sa.setAttrValue(e.getAttrValueList().get(0).getValueName());    // 属性值名称
                return sa;
            }).collect(Collectors.toList());
            goods.setAttrs(list);
        }
        // 将数据添加到ES
        goodsRepository.save(goods);
        return true;
    }

    @Override
    public Boolean offSale(Long skuId) {
        goodsRepository.deleteById(skuId);
        return true;
    }

    @Override
    public Boolean incrHotScore(Long skuId) {
        // 保存到redis
        String hotKey = RedisConstant.SKU_HOT_KEY;
        // 每10次点击操作1次ES
        Double score = redisTemplate.opsForZSet().incrementScore(hotKey, skuId, 1);
        if (score % 10 < 1) {
            // 获取skuId对应的商品
            Goods goods = goodsRepository.findById(skuId).get();
            goods.setHotScore(Math.round(score));
            goodsRepository.save(goods);
        }
        return true;
    }

@Override
@SneakyThrows
public SearchRespVo search(SearchParam searchParam) {
    // 第一步：封装请求条件对象
    SearchRequest searchRequest = buildQueryConditions(searchParam);

    // 第二步：执行查询
    SearchResponse resp = highLevelClient.search(searchRequest, RequestOptions.DEFAULT);

    // 第三步：封装响应
    SearchRespVo searchRespVo = buildSearchResp(resp);

    // 设置分页信息
    searchRespVo.setPageNo(searchParam.getPageNo());
    searchRespVo.setPageSize(searchParam.getPageSize());
    searchRespVo.setTotalPages(
            (searchRespVo.getTotal() + searchParam.getPageSize() - 1) / searchParam.getPageSize()
    );

    return searchRespVo;
}


    /**
     * 封装查询条件
     */
    private SearchRequest buildQueryConditions(SearchParam searchParam) {

        SearchRequest searchRequest = new SearchRequest("goods");

        // 条件构建对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();


        // 创建多条件对象
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        // 设置关键字
        if (!StringUtils.isEmpty(searchParam.getKeyword())) {
            MatchQueryBuilder title = QueryBuilders.matchQuery("title", searchParam.getKeyword())
                    .operator(Operator.AND);
            boolQueryBuilder.must(title);
        } else if (!StringUtils.isEmpty(searchParam.getKeywordInShop())) {
            MatchQueryBuilder title = QueryBuilders.matchQuery("title", searchParam.getKeywordInShop())
                    .operator(Operator.AND);
            boolQueryBuilder.must(title);
        }

        // 过滤品牌 2:华为
        String trademark = searchParam.getTrademark();
        if (!StringUtils.isEmpty(trademark)) {
            String[] split = trademark.split(":");
            if (split.length == 2) {
                TermQueryBuilder tmId = QueryBuilders.termQuery("tmId", split[0]);
                boolQueryBuilder.filter(tmId);
            }
        }

        // 分类过滤
        if (searchParam.getCategory1Id() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category1Id", searchParam.getCategory1Id()));
        }
        if (searchParam.getCategory2Id() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category2Id", searchParam.getCategory2Id()));
        }
        if (searchParam.getCategory3Id() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category3Id", searchParam.getCategory3Id()));
        }

        // 商店过滤
        if (searchParam.getShopId() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("shopId", searchParam.getShopId()));
        }

        // 平台属性 23:4G:运行内存
        String[] props = searchParam.getProps();
        if (props != null) {
            for (String prop : props) {
                String[] split = prop.split(":");
                if (split.length == 3) {
                    // 创建多条件对象
                    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

                    BoolQueryBuilder subBoolQuery = QueryBuilders.boolQuery();
                    subBoolQuery.must(QueryBuilders.termQuery("attrs.attrId", split[0]));
                    subBoolQuery.must(QueryBuilders.termQuery("attrs.attrValue", split[1]));

                    boolQuery.must(QueryBuilders.nestedQuery("attrs", subBoolQuery, ScoreMode.None));

                    boolQueryBuilder.filter(boolQuery);
                }
            }
        }

        // 添加条件
        searchSourceBuilder.query(boolQueryBuilder);


        // 分页
        int idx = (searchParam.getPageNo() - 1) * searchParam.getPageSize();
        searchSourceBuilder.from(idx);
        searchSourceBuilder.size(searchParam.getPageSize());

        // 排序  1:desc
        String order = searchParam.getOrder();
        if (!StringUtils.isEmpty(order)) {
            String[] split = order.split(":");
            searchSourceBuilder.sort(
                    split[0].equals("1") ? "hotScore" : "price",
                    split[1].equals("asc") ? SortOrder.ASC : SortOrder.DESC
            );
        }

        // 聚合——品牌
        TermsAggregationBuilder tmIdAgg = AggregationBuilders.terms("tmIdAgg").field("tmId");
        tmIdAgg.subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName"));
        tmIdAgg.subAggregation(AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogoUrl"));
        searchSourceBuilder.aggregation(tmIdAgg);

        // 聚合——平台属性
        NestedAggregationBuilder attrAgg = AggregationBuilders.nested("attrAgg", "attrs");
        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attrIdAgg").field("attrs.attrId");
        attrAgg.subAggregation(attrIdAgg);
        attrIdAgg.subAggregation(
                AggregationBuilders.terms("attrNameAgg").field("attrs.attrName")
        );
        attrIdAgg.subAggregation(
                AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue")
        );
        searchSourceBuilder.aggregation(attrAgg);

        // 高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.preTags("<span style=\"color: red\">");
        highlightBuilder.postTags("</span>");
        // 结果过滤
        searchSourceBuilder.fetchSource(
                new String[]{"id", "defaultImg", "title", "price", "shopId", "shopName"},
                null
        );


        // 将构建条件对象添加到请求中
        searchRequest.source(searchSourceBuilder);

        System.out.println("\nDSL\n" + searchSourceBuilder + "\n");

        return searchRequest;
    }


    /**
     * 封装响应对象
     */
    private SearchRespVo buildSearchResp(SearchResponse resp) {
        SearchRespVo searchRespVo = new SearchRespVo();

        Map<String, Aggregation> asMap = resp.getAggregations().getAsMap();

        // 品牌数据封装
        ParsedLongTerms tmIdAgg = (ParsedLongTerms) asMap.get("tmIdAgg");
        List<? extends Terms.Bucket> buckets = tmIdAgg.getBuckets();
        if (!CollectionUtils.isEmpty(buckets)) {
            List<SearchRespTmVo> list = buckets.stream().map(e -> {
                        SearchRespTmVo tmResp = new SearchRespTmVo();
                        // 品牌ID
                        tmResp.setTmId(e.getKeyAsNumber().longValue());
                        // 品牌名字
                        ParsedStringTerms tmNameAgg = (ParsedStringTerms) e.getAggregations().asMap().get("tmNameAgg");
                        tmResp.setTmName(tmNameAgg.getBuckets().get(0).getKeyAsString());
                        // 品牌logo
                        ParsedStringTerms tmLogoUrlAgg = (ParsedStringTerms) e.getAggregations().asMap().get("tmLogoUrlAgg");
                        tmResp.setTmLogoUrl(tmLogoUrlAgg.getBuckets().get(0).getKeyAsString());
                        return tmResp;
                    }
            ).collect(Collectors.toList());
            searchRespVo.setTrademarkList(list);
        }

        // 平台属性封装
        ParsedNested attrAgg = (ParsedNested) asMap.get("attrAgg");
        ParsedLongTerms attrIdAgg = (ParsedLongTerms) attrAgg.getAggregations().asMap().get("attrIdAgg");
        List<? extends Terms.Bucket> attrBuckets = attrIdAgg.getBuckets();
        if (!CollectionUtils.isEmpty(attrBuckets)) {
            List<SearchRespAttrVo> list = attrBuckets.stream().map(
                    attrBucket -> {
                        SearchRespAttrVo searchRespAttrVo = new SearchRespAttrVo();
                        // 平台属性ID
                        searchRespAttrVo.setAttrId(attrBucket.getKeyAsNumber().longValue());
                        // 平台属性名
                        Map<String, Aggregation> map = attrBucket.getAggregations().asMap();
                        ParsedStringTerms attrNameAgg = (ParsedStringTerms) map.get("attrNameAgg");
                        searchRespAttrVo.setAttrName(attrNameAgg.getBuckets().get(0).getKeyAsString());
                        // 平台属性值
                        ParsedStringTerms attrValueAgg = (ParsedStringTerms) map.get("attrValueAgg");
                        List<? extends Terms.Bucket> valueBuckets = attrValueAgg.getBuckets();
                        if (!CollectionUtils.isEmpty(valueBuckets)) {
                            List<String> arrtValueList = valueBuckets.stream().map(
                                    MultiBucketsAggregation.Bucket::getKeyAsString
                            ).collect(Collectors.toList());
                            searchRespAttrVo.setAttrValueList(arrtValueList);
                        }
                        return searchRespAttrVo;
                    }
            ).collect(Collectors.toList());
            searchRespVo.setAttrsList(list);
        }

        // 封装商品
        ArrayList<Goods> goodsList = new ArrayList<>();
        SearchHit[] hits = resp.getHits().getHits();
        if (hits != null) {
            for (SearchHit hit : hits) {
                Goods goods = JSONObject.parseObject(hit.getSourceAsString(), Goods.class);
                // 获取高亮数据
                HighlightField title = hit.getHighlightFields().get("title");
                if (title != null) {
                    goods.setTitle(title.getFragments()[0].toString());
                }
                goodsList.add(goods);
            }
        }
        searchRespVo.setGoodsList(goodsList);

        // 分页信息
        searchRespVo.setTotal(resp.getHits().getTotalHits().value);

        return searchRespVo;
    }
}
